package com.sinosoft.hydra.um.services;

import cn.com.sinux.spring.commons.ImageCropHelper;
import cn.com.sinux.spring.commons.SystemConfig;
import cn.com.sinux.spring.exception.BusinessException;
import cn.com.sinux.spring.exception.ServiceException;
import cn.com.sinux.spring.utils.FileUtils;
import cn.com.sinux.spring.utils.ImageUtils;
import cn.com.sinux.spring.utils.SpecificationHelper;
import cn.com.sinux.spring.utils.fileconvert.ConvertEngine;
import cn.com.sinux.spring.utils.fileconvert.ConvertObject;
import com.sinosoft.hydra.logging.LogModules;
import com.sinosoft.hydra.um.constant.BaseConstant;
import com.sinosoft.hydra.um.domain.model.BakUp;
import com.sinosoft.hydra.um.domain.model.DirectoryType;
import com.sinosoft.hydra.um.domain.model.FileInfo;
import com.sinosoft.hydra.um.domain.permission.TDirectory;
import com.sinosoft.hydra.um.domain.permission.TFileConvert;
import com.sinosoft.hydra.um.repositories.TFileConvertDAO;
import com.sinosoft.hydra.um.services.component.bacup.BakupFactoryBean;
import com.sinosoft.hydra.um.services.component.bacup.BakupWorker;
import com.sinosoft.hydra.um.utils.BakUtils;
import com.sinosoft.hydra.um.utils.UploadUtils;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

/**
 * 文件操作相关方法
 */
@Service
public class FileService {

    private static final java.util.List<String> imageTypeCollections = Arrays.asList("jpg", "png", "gif", "bmp");
    private static final java.util.List<String> officeType = Arrays.asList("xls", "xlsx", "ppt", "pptx", "doc", "docx", "pdf");

    @Autowired
    @Qualifier(value = "officeConvertExecutor")
    private ThreadPoolTaskExecutor officeConvertExecutor;
    @Autowired
    @Qualifier(value = "pdfConvertExecutor")
    private ThreadPoolTaskExecutor pdfConvertExecutor;
    @Autowired
    private TFileConvertDAO tFileConvertDAO;
    @Autowired
    private BakupWorker bakupWorker;
    @Autowired
    private DirectoryService directoryService;

    /**
     * @param in       文件输入流
     * @param fileName 文件相对路径
     * @return 上传图片保存路径
     * @throws java.io.IOException
     * @throws org.apache.commons.fileupload.FileUploadException
     */
    @Transactional
    public FileInfo fileUploadAndPersistence(InputStream in, String fileName, BaseConstant.FileType fileType) throws IOException, FileUploadException {
        String path = fileUpload(in, fileName);
        FileInfo fileInfo = new FileInfo();
        fileInfo.setAttachmentName(fileName);
        fileInfo.setFileType(fileType.name());
        fileInfo.setAttachment(path);
        fileInfo.setUploadTime(new Date());
        return fileInfo;
    }

    /**
     * 上传并调整尺寸（图片类型）
     *
     * @param in
     * @param fileName
     * @param fileType
     * @param maxSize
     * @return
     * @throws IOException
     * @throws FileUploadException
     */
    @Transactional
    public FileInfo fileUploadAndResize(InputStream in, String fileName, BaseConstant.FileType fileType,
                                        int maxSize) throws IOException, FileUploadException {
        String path = fileUpload(in, fileName);
        String relativePath = getRelativePath();
        int w = 0, h = 0;
        try {
            BufferedImage bufferedImage = ImageIO.read(new File(relativePath + path));
            w = bufferedImage.getWidth();
            h = bufferedImage.getHeight();
        } catch (Exception ex) {
            w = -1;
            h = -1;
        }
        if (w != -1 && w > maxSize) {
            String destNew = createPath(relativePath, fileName);
            String realPath = relativePath + destNew;
            try {
                ImageUtils.resizeImageWithMaxWidth(relativePath + path, realPath, maxSize);
                h = BigDecimal.valueOf(maxSize)
                        .divide(BigDecimal.valueOf(w)).multiply(BigDecimal.valueOf(h)).intValue();
                w = maxSize;
                path = destNew;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        FileInfo fileInfo = new FileInfo();
        fileInfo.setAttachmentName(fileName);
        fileInfo.setFileType(fileType.name());
        fileInfo.setAttachment(path);
        fileInfo.setUploadTime(new Date());
        fileInfo.setWidth(w);
        fileInfo.setHeight(h);
        return fileInfo;
    }

    /**
     * 调度转换文档
     */
    @Transactional
    public void doConvert() {
        Specification<TFileConvert> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TFileConvert>() {

            public void fillParam(Root<TFileConvert> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("status").as(String.class), BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_WORKING.name()));
                list.add(cb.equal(root.get("resultType").as(String.class), "swf"));
            }
        });
        List<TFileConvert> tFileConverts = tFileConvertDAO.findAll(specification);
        if (tFileConverts.isEmpty()) {
            return;
        }
        for (TFileConvert tFileConvert : tFileConverts) {
            convertFilesToSwf(tFileConvert, null);
        }
    }
//
//    /**
//     * 获取临时文件
//     *
//     * @param fleId 文件的主键
//     * @return
//     */
//    public TFile getTempFile(String fleId) {
//        if (StringUtils.isBlank(fleId)) {
//            return null;
//        }
//        TFile tFile = tFileDAO.findOne(fleId);
//        tFileDAO.delete(fleId);
//        return tFile;
//    }

    /**
     * 删除临时文件
     *
     * @param path 路径
     */
    @Transactional
    public void delTempFile(String path) {
        if (StringUtils.isNotBlank(path)) {
            String relativePath = getRelativePath();
            String absolutePath = relativePath + path;
            File file = new File(absolutePath);
            if (file.exists() && file.exists() && file.delete()) {
                LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件{}被成功删除", absolutePath);
            }
        }
    }

    /**
     * 文件上传
     *
     * @param in 上传流
     * @return 上传图片保存路径
     * @throws IOException
     * @throws FileUploadException
     */
    @Transactional
    public String fileUpload(InputStream in) throws IOException, FileUploadException {
        return fileUpload(in, null);
    }

    /**
     * 文件复制
     *
     * @param source   文件原地址
     * @param fileName 文件名称
     * @return 上传图片保存路径
     * @throws IOException
     * @throws IOException 复制文件跑出IO异常
     */
    private String fileCopy(String source, String fileName) throws IOException {
        String relativePath = getRelativePath();
        String imgUrl = createPath(relativePath, fileName);
        FileUtils.copyFile(new File(relativePath + source), new File(relativePath + imgUrl));
        return imgUrl;
    }

    /**
     * 获取据对路径
     *
     * @return 据对路径
     */
    public String getRelativePath() {
        return SystemConfig.getProperty("trunk.root.path");
    }

    /**
     * 截取图片
     *
     * @param path
     * @param imageCropHelper
     * @return
     */
    public String cropImg(String path, ImageCropHelper imageCropHelper) {
        String _path = getRelativePath() + path;
        File file = new File(_path);
        if (!file.exists()) {
            throw new BusinessException("图片不存在，不能截取");
        }
        String relativePath = getRelativePath();
//        String randomDir = UploadUtils.generateRandomDir();imgurl  == dir
        String imgurl = createPath(relativePath, file.getName());
        String realPath = relativePath + imgurl;
        ImageUtils.cropImage(_path, realPath, imageCropHelper);
        return imgurl;
    }

    /**
     * 创建文件路径
     *
     * @param relativePath 绝对跟路径
     * @param fileName     文件名称
     * @return
     */
    public String createPath(String relativePath, String fileName) {
        // 得到随机目录
        return createDir(relativePath) + UploadUtils.generateRandonFileName(fileName);
    }

    /**
     * 创建文件路径
     *
     * @param relativePath 绝对跟路径
     * @return
     */
    public String createDir(String relativePath) {
        // 得到随机目录
        String randomDir = UploadUtils.generateRandomDir();
        String path = relativePath + "/upload" + randomDir;
        // 得到一个imgurl
        String imgurl = "/upload" + randomDir + "/";
        File pathFile = new File(path);
        //如果目录存在或者创建成功直接返回url，否则返回控制字符串
        imgurl = (pathFile.exists() || pathFile.mkdirs()) ? imgurl : "";
        if (StringUtils.isBlank(imgurl)) {
            throw new ServiceException("创建目录失败");
        }
        return imgurl;
    }

//    /**
//     * 调度转换文档
//     */
//    @Transactional
//    public void doConvertWorkingImg() {
//        Specification<TFileConvert> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TFileConvert>() {
//
//            public void fillParam(Root<TFileConvert> root, CriteriaBuilder cb, List<Predicate> list) {
//                list.add(cb.equal(root.get("status").as(String.class), BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_WORKING.name()));
//                list.add(cb.equal(root.get("resultType").as(String.class), "png"));
//            }
//        });
//        List<TFileConvert> tFileConverts = tFileConvertDAO.findAll(specification);
//        if (tFileConverts.isEmpty()) {
//            return;
//        }
//        convertFilesToImg(tFileConverts, null);
//    }

    /**
     * 获取资源的转换结果
     *
     * @param source     资源
     * @param sourceType 资源类型
     * @param resultType 转换结果的类型 PNG
     * @return
     */
    public TFileConvert findOne(final String source, final String sourceType, final String resultType) {
        Specification<TFileConvert> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TFileConvert>() {

            public void fillParam(Root<TFileConvert> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("source").as(String.class), source));
                list.add(cb.equal(root.get("sourceType").as(String.class), sourceType));
                list.add(cb.equal(root.get("resultType").as(String.class), resultType));
            }
        });
        List<TFileConvert> page = tFileConvertDAO.findAll(specification);
        return (page == null || page.size() == 0) ? null : page.get(page.size() - 1);
    }

    /**
     * 删除转换的资源
     *
     * @param source     资源
     * @param sourceType 资源类型
     */
    @Transactional
    public void removeConvert(final String source, final String sourceType) {
        Specification<TFileConvert> specification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<TFileConvert>() {

            public void fillParam(Root<TFileConvert> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("source").as(String.class), source));
                list.add(cb.equal(root.get("sourceType").as(String.class), sourceType));
            }
        });
        List<TFileConvert> fileConverts = tFileConvertDAO.findAll(specification);
        for (TFileConvert tFileConvert : fileConverts) {
            if (StringUtils.isNotBlank(tFileConvert.getDirectory())) {
                File file = new File(getRelativePath() + tFileConvert.getDirectory());
                if (file.exists() && file.delete()) {
                    LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件已经删除");
                } else {
                    LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件不存在获取删除不了");
                }
                file = new File(getRelativePath() + tFileConvert.getSourcePath());
                if (file.exists() && file.delete()) {
                    LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件已经删除");
                } else {
                    LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件不存在获取删除不了");
                }
            }
            tFileConvertDAO.delete(tFileConvert);
        }
    }

    /**
     * 文档转换接口
     */
    public static interface FileConvertListener {
        public void convertAfter(TFileConvert fileConvert);
    }

    /**
     * 进行文件转换
     *
     * @param fileConvert         待转换的文件
     * @param fileConvertListener 转换文件后回调接口
     */
    @Transactional
    public void convertFilesToSwf(TFileConvert fileConvert, final FileConvertListener fileConvertListener) {
        //验证文件后缀类型
        //首先判断文件类型，如果文件类型不需要转换则不进行转换
        String type = FileUtils.getExtName(fileConvert.getSourcePath());
        if (StringUtils.isBlank(type) || !officeType.contains(type)) {
            LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("文件类型不支持转换");
            return;
        }
        if (findOne(fileConvert.getSource(), fileConvert.getSourceType(), "swf") != null) {
            LoggerFactory.getLogger(LogModules.STD_LOGGER).debug("转换的结果已经存在，不能重复转换");
            if (fileConvertListener != null)
                fileConvertListener.convertAfter(fileConvert);
            return;
        }
        if (StringUtils.isBlank(fileConvert.getId())) {
            fileConvert.setCreateTime(new Date());
            fileConvert.setResultType("swf");
            String directory = fileConvert.getSourcePath().substring(0, fileConvert.getSourcePath().lastIndexOf("/") + 1) + UUID.randomUUID().toString();
            new File(getRelativePath() + directory).mkdirs();
            //根据资源路径创建一个目录
            fileConvert.setDirectory(directory);
            fileConvert.setStatus(BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_WORKING.name());
            tFileConvertDAO.save(fileConvert);
        }
        //开始异步转换
        Map<ConvertEngine.EngineConfig, String> map = new HashMap<ConvertEngine.EngineConfig, String>();
        map.put(ConvertEngine.EngineConfig.OPENOFFICE_HOME, SystemConfig.getProperty("openOffice.home"));
        map.put(ConvertEngine.EngineConfig.OPENOFFICE_PORT, SystemConfig.getProperty("openOffice.server.port", "8100"));
        map.put(ConvertEngine.EngineConfig.PDFCREATOR_HOME, SystemConfig.getProperty("trunk.root.path") + "/file/pdf2swf.exe");
        ConvertObject convertObject = new ConvertObject(getRelativePath() + fileConvert.getSourcePath(), getRelativePath() + fileConvert.getDirectory());
        ConvertEngine.doConvert(ConvertEngine.EngineType.OPENOFFICE, ConvertEngine.ConvertType.toSwf,
                convertObject, new HashMap<ConvertEngine.EngineConfig, String>());
        if (ConvertEngine.ConvertStatus.SUCCESS.equals(convertObject.getConvertStatus())) {
            if (convertObject.getResults().isEmpty()) {
                fileConvert.setStatus(BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_FAILURE.name());
            } else {
                fileConvert.setStatus(BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_SUCCESS.name());
                fileConvert.setResult(StringUtils.join(convertObject.getResults(), ","));
            }
            if (fileConvertListener != null) {
                fileConvertListener.convertAfter(fileConvert);
            }
        } else {
            fileConvert.setStatus(BaseConstant.FileConvertStatus.FILE_CONVERT_STATUS_FAILURE.name());
        }
        tFileConvertDAO.save(fileConvert);
    }

    /**
     * 上传文档
     *
     * @param in
     * @param fileName
     * @return
     * @throws IOException
     * @throws FileUploadException
     */
    public String fileUpload(InputStream in, String fileName) throws IOException, FileUploadException {
        String relativePath = getRelativePath();
//        String randomDir = UploadUtils.generateRandomDir();imgurl  == dir
        String imgurl = createPath(relativePath, fileName);
        String realPath = relativePath + imgurl;
        String fileType = FileUtils.getExtName(imgurl);
        if (SystemConfig.getProperty("filtered.name", "exe,bat,sh").contains(fileType.toLowerCase())) {
            throw new BusinessException("文件格式不符合要求！");
        }
//        FileCopyUtils.copy(in, new FileOutputStream(realPath));
        FileUtils.write(in, realPath);
        return imgurl;
    }

    /**
     * 查询所有备份
     *
     * @return
     */
    public List<BakUp> listBak() {
        List<BakUp> names = new ArrayList<BakUp>();
        final String destPath = getRelativePath() + "/bak";
        File file = new File(destPath);
        File[] files = file.listFiles();
        for (File file1 : files) {
            names.add(new BakUp(file1.getName(), new Date(file1.lastModified())));
        }
        Collections.sort(names, new Comparator<BakUp>() {

            public int compare(BakUp o1, BakUp o2) {
                return o2.getBakTime().compareTo(o1.getBakTime());
            }
        });
        return names;
    }

    /**
     * 删除备份
     *
     * @param name
     * @return
     */
    public String delBak(String name) {
        final String destPath = getRelativePath() + "/bak/" + name;
        File file = new File(destPath);
        if (!file.exists()) {
            throw new BusinessException("#文件不存在，无法删除");
        }
        try {
            FileUtils.deleteDirectory(file);
        } catch (IOException e) {
            throw new BusinessException("#删除文件出错，无法删除");
        }
        return name;
    }

    /**
     * 进行数据备份
     *
     * @return
     */
    public String bakup() {
        TDirectory directory = directoryService.findByKey(DirectoryType.valueOf("DIRECTORY_BAKING"), null);
        if (directory != null) {
            if ("bak".equals(directory.getDirValue())) {
                throw new BusinessException("#正在进行数据备份，不能重复执行");
            } else if ("recovery".equals(directory.getDirValue())) {
                throw new BusinessException("#正在进行数据恢复，不能执行备份");
            }
        }
        directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "true");
        String name = new Date().getTime() + "";
        final String destPath = getRelativePath() + "/bak/" + name;
        File file = new File(destPath);
        file.mkdirs();
        bakupWorker.bakUp(destPath + "/bakSql.sql", new BakupFactoryBean.BakupListener() {
            public void bakupSuccess() {
                try {
                    BakUtils.fileBackup(destPath + "/bakFile");
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "none");
                }
            }

            public void bacupFail(Exception ex) {
                directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "none");
                throw new BusinessException("#数据备份失败:" + ex.getMessage());
            }
        });
        return name;
    }

    /**
     * 进行数据恢复
     *
     * @param name
     */
    public void recovery(String name) {
        TDirectory directory = directoryService.findByKey(DirectoryType.valueOf("DIRECTORY_BAKING"), null);
        if (directory != null) {
            if ("bak".equals(directory.getDirValue())) {
                throw new BusinessException("#正在进行数据备份，不能执行备份");
            } else if ("recovery".equals(directory.getDirValue())) {
                throw new BusinessException("#正在进行数据恢复，不能重复执行");
            }
        }
        directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "recovery");
        final String destPath = SystemConfig.getProperty("trunk.root.path") + "/bak/" + name;
        File file = new File(destPath);
        if (!file.exists()) {
            directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "none");
            throw new BusinessException("#数据恢复失败，备份的文件已不存在");
        }
        bakupWorker.recoveryUp(destPath + "/bakSql.sql", new BakupFactoryBean.RecoveryListener() {
            public void recoverySuccess() {
                try {
                    BakUtils.fileRecover(destPath + "/bakFile");
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "none");
                }
            }


            public void recoveryFail(Exception ex) {
                directoryService.addDirectory(DirectoryType.valueOf("DIRECTORY_BAKING"), "none");
                throw new BusinessException("#数据恢复失败:" + ex.getMessage());
            }
        });
    }
}
